blob: 845c55dc6d285581f1912ebdf855b5d3cb59d6ac [file] [log] [blame]
Soares Chene5a65672017-07-21 10:08:301<!doctype html>
2<meta charset=utf-8>
3<title>RTCPeerConnection.prototype.peerIdentity</title>
4<script src="/resources/testharness.js"></script>
5<script src="/resources/testharnessreport.js"></script>
6<script src="identity-helper.js"></script>
7<script>
8 'use strict';
9
10 // Test is based on the following editor draft:
11 // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
12
13 // The tests here interacts with the mock identity provider located at
14 // /.well-known/idp-proxy/mock-idp.js
15
16 // The following helper functions are called from identity-helper.js
17 // parseAssertionResult
18 // getIdpDomains
19 // assert_rtcerror_rejection
20 // hostString
21
22 /*
23 9.6. RTCPeerConnection Interface Extensions
24 partial interface RTCPeerConnection {
25 void setIdentityProvider(DOMString provider,
26 optional RTCIdentityProviderOptions options);
27 Promise<DOMString> getIdentityAssertion();
28 readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
29 readonly attribute DOMString? idpLoginUrl;
30 readonly attribute DOMString? idpErrorInfo;
31 };
32
33 dictionary RTCIdentityProviderOptions {
34 DOMString protocol = "default";
35 DOMString usernameHint;
36 DOMString peerIdentity;
37 };
38
39 [Constructor(DOMString idp, DOMString name)]
40 interface RTCIdentityAssertion {
41 attribute DOMString idp;
42 attribute DOMString name;
43 };
44 */
45
46 /*
47 4.3.2. setRemoteDescription
48 If an a=identity attribute is present in the session description, the browser
49 validates the identity assertion..
50
51 If the "peerIdentity" configuration is applied to the RTCPeerConnection, this
52 establishes a target peer identity of the provided value. Alternatively, if the
53 RTCPeerConnection has previously authenticated the identity of the peer (that
54 is, there is a current value for peerIdentity ), then this also establishes a
55 target peer identity.
56 */
57 promise_test(() => {
58 const pc1 = new RTCPeerConnection();
59 const pc2 = new RTCPeerConnection();
60
61 const port = window.location.port;
62 const [idpDomain] = getIdpDomains();
63 const idpHost = hostString(idpDomain, port);
64
65 pc1.setIdentityProvider(idpHost, {
66 protocol: 'mock-idp.js',
67 usernameHint: `alice@${idpDomain}`
68 });
69
70 return pc1.createOffer()
71 .then(offer => pc2.setRemoteDescription(offer))
72 .then(() => pc2.peerIdentity)
73 .then(identityAssertion => {
74 const { idp, name } = identityAssertion;
75 assert_equals(idp, idpDomain, `Expect IdP domain to be ${idpDomain}`);
76 assert_equals(identityAssertion, `alice@${idpDomain}`,
77 `Expect validated identity from mock-idp.js to be same as specified in usernameHint`);
78 });
79 }, 'setRemoteDescription() on offer with a=identity should establish peerIdentity');
80
81 /*
82 4.3.2. setRemoteDescription
83 The target peer identity cannot be changed once set. Once set, if a different
84 value is provided, the user agent MUST reject the returned promise with a newly
85 created InvalidModificationError and abort this operation. The RTCPeerConnection
86 MUST be closed if the validated peer identity does not match the target peer
87 identity.
88 */
89 promise_test(t => {
90 const port = window.location.port;
91 const [idpDomain] = getIdpDomains();
92 const idpHost = hostString(idpDomain, port);
93
94 const pc1 = new RTCPeerConnection();
95 const pc2 = new RTCPeerConnection({
96 peerIdentity: `bob@${idpDomain}`
97 });
98
99 pc1.setIdentityProvider(idpHost, {
100 protocol: 'mock-idp.js',
101 usernameHint: `alice@${idpDomain}`
102 });
103
104 return pc1.createOffer()
105 .then(offer =>
106 promise_rejects(t, 'InvalidModificationError',
107 pc2.setRemoteDescription(offer)))
108 .then(() => {
109 assert_true(pc2.signalingState, 'closed',
110 'Expect peer connection to be closed after mismatch peer identity');
111 });
112 }, 'setRemoteDescription() on offer with a=identity that resolve to value different from target peer identity should reject with InvalidModificationError');
113
114 /*
115 9.4. Verifying Identity Assertions
116 8. The RTCPeerConnection decodes the contents and validates that it contains a
117 fingerprint value for every a=fingerprint attribute in the session description.
118 This ensures that the certificate used by the remote peer for communications
119 is covered by the identity assertion.
120
121 If identity validation fails, the peerIdentity promise is rejected with a newly
122 created OperationError.
123
124 If identity validation fails and there is a target peer identity for the
125 RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
126 with the same DOMException.
127 */
128 promise_test(t => {
129 const port = window.location.port;
130 const [idpDomain] = getIdpDomains();
131 const idpHost = hostString(idpDomain, port);
132
133 const pc1 = new RTCPeerConnection();
134 const pc2 = new RTCPeerConnection({
135 peerIdentity: `alice@${idpDomain}`
136 });
137
138 // Ask mockidp.js to return custom contents in validation result
139 pc1.setIdentityProvider(idpHost, {
140 protocol: 'mock-idp.js?validatorAction=return-custom-contents&contents=bogus',
141 usernameHint: `alice@${idpDomain}`
142 });
143
144 const peerIdentityPromise = pc2.peerIdentity;
145
146 return pc1.createOffer()
147 .then(offer => Promise.all([
148 promise_rejects(t, 'OperationError',
149 pc2.setRemoteDescription(offer)),
150 promise_rejects(t, 'OperationError',
151 peerIdentityPromise)
152 ]));
153 }, 'setRemoteDescription() with peerIdentity set and with IdP proxy that return validationAssertion with mismatch contents should reject with OperationError');
154
155 /*
156 9.4. Verifying Identity Assertions
157 9. The RTCPeerConnection validates that the domain portion of the identity matches
158 the domain of the IdP as described in [RTCWEB-SECURITY-ARCH]. If this check
159 fails then the identity validation fails.
160 */
161 promise_test(t => {
162 const port = window.location.port;
163 const [idpDomain1, idpDomain2] = getIdpDomains();
164 assert_not_equals(idpDomain1, idpDomain2,
165 'Sanity check two idpDomains are different');
166
167 const idpHost1 = hostString(idpDomain1, port);
168
169 const pc1 = new RTCPeerConnection();
170 const pc2 = new RTCPeerConnection({
171 peerIdentity: `alice@${idpDomain2}`
172 });
173
174 // mock-idp.js will return assertion of domain2 identity
175 // with domain1 in the idp.domain field
176 pc1.setIdentityProvider(idpHost1, {
177 protocol: 'mock-idp.js',
178 usernameHint: `alice@${idpDomain2}`
179 });
180
181 return pc1.getIdentityAssertion()
182 .then(assertionResultStr => {
183 const { idp, assertion } = parseAssertionResult(assertionResultStr);
184
185 assert_equals(idp.domain, idpDomain1,
186 'Sanity check domain of assertion is domain1');
187
188 assert_equals(assertion.options.usernameHint, `alice@${idpDomain2}`,
189 'Sanity check domain1 is going to validate a domain2 identity');
190
191 return pc1.createOffer();
192 })
193 .then(offer => Promise.all([
194 promise_rejects(t, 'OperationError',
195 pc2.setRemoteDescription(offer)),
196 promise_rejects(t, 'OperationError',
197 pc2.peerIdentity)
198 ]));
199 }, 'setRemoteDescription() and peerIdentity should reject with OperationError if IdP return validated identity that is different from its own domain');
200
201 /*
202 9.4 Verifying Identity Assertions
203 If identity validation fails and there is a target peer identity for the
204 RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
205 with the same DOMException.
206
207 9.5 IdP Error Handling
208 - If an identity provider throws an exception or returns a promise that is ultimately
209 rejected, then the procedure that depends on the IdP MUST also fail. These types of
210 errors will cause an IdP failure with an RTCError with errorDetail set to
211 "idp-execution-failure".
212
213 Any error generated by the IdP MAY provide additional information in the
214 idpErrorInfo attribute. The information in this string is defined by the
215 IdP in use.
216 */
217 promise_test(t => {
218 const port = window.location.port;
219 const [idpDomain] = getIdpDomains();
220 const idpHost = hostString(idpDomain, port);
221
222 const pc1 = new RTCPeerConnection();
223 const pc2 = new RTCPeerConnection({
224 peerIdentity: `alice@${idpDomain}`
225 });
226
227 // Ask mock-idp.js to throw error during validation,
228 // i.e. during pc2.setRemoteDescription()
229 pc1.setIdentityProvider(idpHost, {
230 protocol: 'mock-idp.js?validatorAction=throw-error&errorInfo=bar',
231 usernameHint: `alice@${idpDomain}`
232 });
233
234 return pc1.createOffer()
235 .then(offer => Promise.all([
236 assert_rtcerror_rejection('idp-execution-failure',
237 pc2.setRemoteDescription(offer)),
238 assert_rtcerror_rejection('idp-execution-failure',
239 pc2.peerIdentity)
240 ]))
241 .then(() => {
242 assert_equals(pc2.idpErrorInfo, 'bar',
243 'Expect pc2.idpErrorInfo to be set to the err.idpErrorInfo thrown by mock-idp.js');
244 });
245 }, `When IdP throws error and pc has target peer identity, setRemoteDescription() and peerIdentity rejected with RTCError('idp-execution-error')`);
246
247 /*
248 4.3.2. setRemoteDescription
249 If there is no target peer identity, then setRemoteDescription does not await the
250 completion of identity validation.
251
252 9.5. IdP Error Handling
253 - If an identity provider throws an exception or returns a promise that is
254 ultimately rejected, then the procedure that depends on the IdP MUST also fail.
255 These types of errors will cause an IdP failure with an RTCError with errorDetail
256 set to "idp-execution-failure".
257
258 9.4. Verifying Identity Assertions
259 If identity validation fails and there is no a target peer identity, the value of
260 the peerIdentity MUST be set to a new, unresolved promise instance. This permits
261 the use of renegotiation (or a subsequent answer, if the session description was
262 a provisional answer) to resolve or reject the identity.
263 */
264 promise_test(t => {
265 const pc1 = new RTCPeerConnection();
266 const pc2 = new RTCPeerConnection();
267
268 const port = window.location.port;
269 const [idpDomain] = getIdpDomains();
270 const idpHost = hostString(idpDomain, port);
271
272 // Ask mock-idp.js to throw error during validation,
273 // i.e. during pc2.setRemoteDescription()
274 pc1.setIdentityProvider(idpHost, {
275 protocol: 'mock-idp.js?validatorAction=throw-error',
276 usernameHint: `alice@${idpDomain}`
277 });
278
279 const peerIdentityPromise1 = pc2.peerIdentity;
280
281 return pc1.createOffer()
282 .then(offer =>
283 // setRemoteDescription should succeed because there is no target peer identity set
284 pc2.setRemoteDescription(offer))
285 .then(() =>
286 assert_rtcerror_rejection('idp-execution-failure',
287 peerIdentityPromise,
288 `Expect first peerIdentity promise to be rejected with RTCError('idp-execution-failure')`))
289 .then(() => {
290 const peerIdentityPromise2 = pc2.peerIdentity;
291 assert_not_equals(peerIdentityPromise2, peerIdentityPromise1,
292 'Expect pc2.peerIdentity to be replaced with a fresh unresolved promise');
293
294 // regenerate an identity assertion with no test option to throw error
295 pc1.setIdentityProvider(idpHost, {
296 protocol: 'idp-test.js',
297 usernameHint: `alice@${idpDomain}`
298 });
299
300 return pc1.createOffer()
301 .then(offer => pc2.setRemoteDescription(offer))
302 .then(peerIdentityPromise2)
303 .then(identityAssertion => {
304 const { idp, name } = identityAssertion;
305
306 assert_equals(idp, idpDomain,
307 `Expect IdP domain to be ${idpDomain}`);
308
309 assert_equals(name, `alice@${idpDomain}`,
310 `Expect validated identity to be alice@${idpDomain}`);
311
312 assert_equals(pc2.peeridentity, peerIdentityPromise2,
313 'Expect pc2.peerIdentity to stay fixed after identity is validated');
314 });
315 });
316 }, 'IdP failure with no target peer identity should have following setRemoteDescription() succeed and replace pc.peerIdentity with a new promise');
317
318</script>